#region Using References

using System.Collections.Generic;
using System.IO;
using Beaker.OpenCube.Specs.Builders;
using Beaker.Services.FileSystem;
using Ionic.Zip;

#endregion

namespace Beaker.Services.Specs.Builders
{
	public class ZipFileBuilder
		: Builder<IFile>
	{
		private IFolder _parentFolder;
		private string _fileName;
		private IFolder _sourceFolder;

		public ZipFileBuilder WithFilename( string name )
		{
			_fileName = name;
			return this;
		}

		public ZipFileBuilder WithParentFolder( IFolder folder )
		{
			_parentFolder = folder;
			return this;
		}

		public ZipFileBuilder WithFilesFrom( IFolder folderWithFiles, bool includeFolderItself = false )
		{
			_sourceFolder = folderWithFiles;
			return this;
		}

		protected override IFile OnBuild()
		{
			var zipStream = new MemoryStream();
			using( var zipFile = new ZipFile() )
			{
				if( _sourceFolder != null )
				{
					CopyFilesToZip( zipFile, _sourceFolder );
				}

				zipFile.Save( zipStream );
			}

			zipStream.Flush();
			zipStream.Position = 0;
			byte[] content = zipStream.ToArray();

			return new MemoryStreamFile( _parentFolder, _fileName, content );
		}

		private void CopyFilesToZip( ZipFile zipFile, IFolder sourceFolder, string parentPath = "" )
		{
			IEnumerable<IFile> files = sourceFolder.GetFiles();

			foreach( IFile file in files )
			{
				IFile f = file;
				zipFile.AddEntry( Path.Combine( parentPath, file.Name ), n => f.OpenRead(), ( n, s ) => s.Close() );
			}

			foreach( IFolder folder in sourceFolder.GetFolders() )
			{
				string folderPath = Path.Combine( parentPath, folder.Name );
				CopyFilesToZip( zipFile, folder, folderPath );
			}
		}

		private class MemoryStreamFile
			: IFile
		{
			private readonly IFolder _parentFolder;
			private readonly string _name;
			private readonly byte[] _content;

			public MemoryStreamFile( IFolder parentFolder, string name, byte[] content )
			{
				_parentFolder = parentFolder;
				_name = name;
				_content = content;
			}

			/// <summary>
			/// Gets the name of the entry
			/// </summary>
			public string Name
			{
				get { return _name; }
			}

			/// <summary>
			/// Get the folder this entry is in
			/// </summary>
			public IFolder ParentFolder
			{
				get { return _parentFolder; }
			}

			/// <summary>
			/// Open the file for reading
			/// </summary>
			/// <returns>A stream of the content of the file</returns>
			public Stream OpenRead()
			{
				var stream = new MemoryStream( _content );
				return stream;
			}

			/// <summary>
			/// Open the text-file for reading
			/// </summary>
			/// <returns>A text-reader with the content of the file</returns>
			public TextReader OpenText()
			{
				return new StreamReader( OpenRead() );
			}
		}
	}
}